package net.edwardstx.brian.rpn.types;

import net.edwardstx.brian.rpn.exception.InvalidComparison;
import net.edwardstx.brian.rpn.exception.InvalidSyntax;
import net.edwardstx.brian.rpn.type.annotations.RPNOperation;

@SuppressWarnings("rawtypes")
public class RPNInteger extends RPNNumeric<Long>{
	public static final String TYPE_NAME = "Integer";
	public static final Type<RPNInteger> TYPE = new Type<RPNInteger>(TYPE_NAME,RPNInteger.class);

	public RPNInteger(Long value) {
		super(value);
	}
	
	public RPNInteger(Integer value) {
		super(value.longValue());
	}
	
	public static RPNInteger parse(String value)throws InvalidSyntax{
		try{
			return new RPNInteger(Long.parseLong(value));
		} catch (Exception e){
			throw new InvalidSyntax();
		}
	}

	@RPNOperation(value="+")
	public static RPNInteger addition(RPNInteger l_0, RPNInteger l_1 ){
		return new RPNInteger(l_0.value + l_1.value);
	}
	
	@RPNOperation(value="-")
	public static RPNInteger subtraction(RPNInteger l_0, RPNInteger l_1 ){
		return new RPNInteger(l_1.value - l_0.value);
	}
	
	@RPNOperation(value="*")
	public static RPNInteger multiply(RPNInteger l_0, RPNInteger l_1 ){
		return new RPNInteger(l_0.value * l_1.value);
	}
	
	
	@RPNOperation(value="/")
	public static RPNNumeric divide(RPNInteger l_0, RPNInteger l_1 ){
		if((l_1.value % l_0.value) == 0)
			return new RPNInteger(l_1.value/ l_0.value);
		return new RPNRational(l_1.value,l_0.value);
	}
	
	@RPNOperation(value="INV")
	public static RPNRational inv(RPNInteger l_0 ){
		return new RPNRational(1L,l_0.value);
	}
	
	@RPNOperation(value="MAX")
	public static RPNInteger max(RPNInteger l_0, RPNInteger l_1 ){
		return new RPNInteger(Math.max(l_0.value, l_1.value));
	}
	
	@RPNOperation(value="MIN")
	public static RPNInteger min(RPNInteger l_0, RPNInteger l_1 ){
		return new RPNInteger(Math.min(l_0.value, l_1.value));
	}
	
	@RPNOperation(value="%")
	public static RPNInteger modulus(RPNInteger l_0, RPNInteger l_1 ){
			return new RPNInteger(l_1.value % l_0.value);
	}
	
	@Override
	public int compareTo(RPNNumeric o) {
		if(o instanceof RPNInteger){
			Long rhs = o.getValue().longValue();
			return value.compareTo(rhs);
		}
		throw new InvalidComparison();
	}

	@Override
	public Type<RPNInteger> getAssociatedType() {
		return TYPE;
	}
	
	@Override
	public String toString(){
		return value.toString();
	}
	
	
	public RPNInteger clone(){
		return new RPNInteger(value);
	}

}
